/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Compilador.Semantico;

import Compilador.Sintatico.ArvoreSintatica.NoArvoreSintatica;
import java.util.ArrayList;
import java.util.HashMap;

/**
 *
 * @author Rafa
 */
public class TabelaSemantica {

    private HashMap<Integer, ArrayList<AtributoTabela>> atributos;
    private int escopoAtual;

    public int getEscopoAtual() {
        return escopoAtual;
    }

    public TabelaSemantica() throws SemanticoException {
        atributos = new HashMap<Integer, ArrayList<AtributoTabela>>();
        escopoAtual = 1;
        inserePrint();
    }
    
    private void inserePrint() throws SemanticoException{
        AtributoTabelaFunction att = new AtributoTabelaFunction(1, TypeVar.Function, 1, 0, 0, "print");
        insertAtributo(att, false);
    }

    public void atualizaValorRetornoFuncao(TypeVar valorRetorno) {
        ArrayList<AtributoTabela> att = atributos.get(escopoAtual - 1);
        for (int count = att.size() - 1; count >= 0; count--) {
            if (att.get(count) instanceof AtributoTabelaFunction) {
                AtributoTabelaFunction attFunction = (AtributoTabelaFunction) att.get(count);
                attFunction.setTypeRetorno(valorRetorno);
                return;
            }
        }
    }

    //Método que insere um atributo na tabela, caso o boolean local seja true, inserirá no escopo local
    public boolean insertAtributo(AtributoTabela atributo, boolean local) throws SemanticoException {
        int escopo = 1;
        if (local) {
            escopo = escopoAtual;
        }
        if (!atributos.containsKey(escopoAtual)) {
            ArrayList<AtributoTabela> atributosEscopo = new ArrayList<AtributoTabela>();
            atributosEscopo.add(atributo);
            atributos.put(escopo, atributosEscopo);
            return true;
        } else {
            AtributoTabela att = existeAtributoEscopo(atributo, escopo);
            if (att != null) {
                if (att.getType() != atributo.getType()) {
                    throw new SemanticoException("Variável já declarada nesse escopo com um tipo diferente", atributo.getLinha(), atributo.getColuna(), atributo.getNameVar());
                }
                return true;
            } else {
                atributos.get(escopo).add(atributo);
                return true;
            }
        }
    }

    public void incrementaEscopo() {
        escopoAtual++;
        ArrayList<AtributoTabela> atributosEscopo = new ArrayList<AtributoTabela>();
        atributos.put(escopoAtual, atributosEscopo);

    }

    public void removeEscopo() {
        atributos.remove(escopoAtual);
        escopoAtual--;
    }

    //Busca o atributo do escopo mais alto
    public AtributoTabela getAtributo(NoArvoreSintatica no) throws SemanticoException {
        int escopo = escopoAtual;
        while (escopo > 0) {
            ArrayList<AtributoTabela> att = atributos.get(escopo);
            if (att != null) {
                for (int count = 0; count < att.size(); count++) {
                    if (att.get(count).getNameVar().equals(no.getNoDescricao())) {
                        return att.get(count);
                    }
                }
            }
            escopo--;
        }
        throw new SemanticoException("Variável não foi declarada", no.getLinha(), no.getColuna(), no.getNoDescricao());


    }

    private AtributoTabela existeAtributoEscopo(AtributoTabela atributo, int escopo) {
        ArrayList<AtributoTabela> att = atributos.get(escopo);
        for (int count = 0; count < att.size(); count++) {
            if (att.get(count).getNameVar().equals(atributo.getNameVar()) && att.get(count).getType() != atributo.getType()) {
                return att.get(count);
            }
        }
        return null;

    }
}
